home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_319 / cnewssrc / cnews.src.lzh / relay / control.c < prev    next >
C/C++ Source or Header  |  1989-07-28  |  8KB  |  262 lines

  1. /*
  2.  * Implement the Usenet control messages, as per RFC 1036 (nee 850).
  3.  * These are fairly infrequent and can afford to be done by
  4.  * separate programs.  They are:
  5.  *
  6.  * cancel message-ID    restricted to Sender: else From: or root, in theory
  7.  * ihave message-ID-list remotesys    generate a sendme from message-ID-list
  8.  * sendme message-ID-list remotesys    send articles named to remotesys
  9.  * (ihave/sendme is semi-documented in the RFCs, kludgey and broken
  10.  * in B2.10.)
  11.  *
  12.  * newgroup groupname    must be Approved:
  13.  * rmgroup groupname    must be Approved:; allow some local control over this
  14.  * sendsys                mail to Reply-To: else From:
  15.  * senduuname            ditto
  16.  * version                ditto
  17.  */
  18.  
  19. #include <stdio.h>
  20. #include <ctype.h>
  21. #ifndef AMIGA
  22. #  include <sys/types.h>
  23. #endif /* AMIGA */
  24.  
  25. #include "libc.h"
  26. #include "news.h"
  27. #include "config.h"
  28. #include "headers.h"
  29. #include "article.h"
  30. #include "caches.h"
  31. #include "history.h"
  32.  
  33. #define NO_FILES ""
  34. #define SUBDIR binfile("ctl")        /* holds shell scripts */
  35.  
  36. /*
  37.  * These are shell meta-characters, except for /, which is included
  38.  * since it allows people to escape from the control directory.
  39.  */
  40. #define SHELLMETAS "<>|&;({$=*?[`'\"/"
  41.  
  42. /* imports from news */
  43. extern statust snufffiles(); 
  44. extern void ihave(), sendme();
  45.  
  46. /* forwards */
  47. FORWARD statust cancelart();
  48. FORWARD void runctlmsg(), bombctlmsg();
  49.  
  50. /*
  51.  * Implement control message specified in "art".
  52.  * Because newgroup and rmgroup may modify the active file, for example,
  53.  * we must flush in-core caches to disk first and reload them afterward.
  54.  * We handle cancels in this process for speed and dbm access.
  55.  * We handle ihave & sendme in this process for dbm access and
  56.  * to work around syntax restrictions (<>).
  57.  *
  58.  * In future, one could pass header values to scripts as arguments or
  59.  * in environment, as NEWS* variables, to save time in the scripts.
  60.  */
  61.  
  62. void ctlmsg(art)
  63. struct article *art;
  64. {
  65.     int pid, deadpid;
  66.     int wstatus;
  67.     char *inname = art->a_tmpf, *ctlcmd = art->h.h_ctlcmd;
  68.     static char nmcancel[] = "cancel ";
  69.     static char nmihave[] = "ihave ";
  70.     static char nmsendme[] = "sendme ";
  71.  
  72.     if (STREQN(ctlcmd, nmcancel, STRLEN(nmcancel))) {
  73.         art->a_status |= cancelart(ctlcmd + STRLEN(nmcancel));
  74.         return;
  75.     }
  76.     if (STREQN(ctlcmd, nmihave, STRLEN(nmihave))) {
  77.         ihave(ctlcmd + STRLEN(nmihave), art);
  78.         return;
  79.     }
  80.     if (STREQN(ctlcmd, nmsendme, STRLEN(nmsendme))) {
  81.         sendme(ctlcmd + STRLEN(nmsendme), art);
  82.         return;
  83.     }
  84.  
  85.     /*
  86.      *    These "other" ctlmsgs are just plain not handled on an Amiga.
  87.      *    I did this primarily so that I wouldn't have to write the
  88.      *    fork() and system() code that Manx left out of the library.
  89.      *    What a bunch of dildoes!!
  90.      *
  91.      *    I took this #ifdef out since I'm going to write a stub for
  92.      *    the fork(), system(), popen(), and pclose() routines so that
  93.      *    I can determine how these routines need to be implemented (at
  94.      *    least, implemented here).
  95.      */
  96. #ifndef AMIGA
  97.     art->a_status |= synccaches();
  98.     (void) fflush(stdout);
  99.     (void) fflush(stderr);
  100.  
  101.     pid = fork();
  102.     if (pid == 0)                /* child? */
  103.         runctlmsg(ctlcmd, inname);
  104.     else if (pid == -1)
  105.         warning("fork failed", "");
  106.  
  107.     /* lint complains about &wstatus on 4.2+BSD; too bad, lint's wrong. */
  108.     while ((deadpid = wait(&wstatus)) != pid && deadpid != -1)
  109.         ;
  110.  
  111.     /* wrong kid returned, fork failed or child screwed up? */
  112.     if (deadpid == -1 || pid == -1 || wstatus != 0)
  113.         art->a_status |= ST_DROPPED;    /* admin got err.msg. by mail */
  114.     art->a_status |= loadcaches();
  115. #endif /* AMIGA */
  116. }
  117.  
  118. STATIC boolean safecmd(cmd)            /* true if it's safe to system(3) cmd */
  119. char *cmd;
  120. {
  121.     register char *s;
  122.  
  123.     printf("** shouldn't be in %s!\n", __FUNC__);
  124.     for (s = cmd; *s != '\0'; s++)
  125.         if (STREQN(s, "..", STRLEN("..")))
  126.             return NO;
  127.     for (s = SHELLMETAS; *s != '\0'; s++)
  128.         if (index(cmd, *s) != NULL)
  129.             return NO;
  130.     return YES;
  131. }
  132.  
  133. /*
  134.  * In theory (RFC 1036 nee 850), we should verify that the user issuing
  135.  * the cancel (the Sender: of this article or From: if no Sender) is the
  136.  * Sender: or From: of the original article or the local super-user.
  137.  *
  138.  * In practice, this is a lot of work and since anyone can forge news
  139.  * (and thus cancel anything), not worth the effort.
  140.  *
  141.  * Ignore ST_ACCESS while cancelling an already-seen article since the
  142.  * article may have been cancelled before or may have a fake history entry
  143.  * because the cancel arrived before the article.
  144.  *
  145.  * If the article being cancelled has not been seen yet, generate a history
  146.  * file entry for the cancelled article in case it arrives after the cancel
  147.  * control.  The history file entry will cause the cancelled article to be
  148.  * rejected as a duplicate.
  149.  */
  150.  
  151. STATIC statust cancelart(msgidstr)
  152. char *msgidstr;
  153. {
  154.     register char *wsp;
  155.     register char *msgid = strsave(msgidstr);
  156.     register int idbytes;
  157.     register char *wholemsgid = msgid;
  158.     register statust status = ST_OKAY;
  159.  
  160.     /* skip leading whitespace in msgid */
  161.     while (*msgid != '\0' && isascii(*msgid) && isspace(*msgid))
  162.         ++msgid;
  163.     /* replace trailing whitespace with NULs; `wsp >= msgid' is not safe */
  164.     idbytes = strlen(msgid);
  165.     for (wsp = msgid + idbytes - 1; idbytes-- > 0 &&
  166.                         isascii(*wsp) && isspace(*wsp); --wsp)
  167.         *wsp = '\0';
  168.  
  169.     if (alreadyseen(msgid)) {
  170.         register char *histent, *filelist;
  171.  
  172.         histent = gethistory(msgid);
  173.         if (histent != NULL && (filelist = findfiles(histent)) != NULL)
  174.             status |= snufffiles(filelist) & ~ST_ACCESS;
  175.     } else {
  176.         status |= fakehist(msgid, DEFEXP, NO_FILES);    /* start log */
  177.         (void) putchar('\n');        /* end log line */
  178.     }
  179.     free(wholemsgid);
  180.     return status;
  181. }
  182.  
  183. /*
  184.  * Execute a non-builtin control message by searching $NEWSCTL/bin and
  185.  * $NEWSBIN/ctl for the command named by the control message.
  186.  * runctlmsg is called from a child of relaynews, so it must always
  187.  * call _exit() rather than exit() to avoid flushing stdio buffers.
  188.  *
  189.  * Enforce at least minimal security: the environment was standardised at
  190.  * startup, including PATH and IFS; close non-standard file descriptors;
  191.  * reject shell metacharacters in ctlcmd.
  192.  */
  193.  
  194. STATIC void runctlmsg(ctlcmd, inname)            /* child process */
  195. register char *ctlcmd, *inname;
  196. {
  197.     register char *cmd;
  198.     register int cmdstat;
  199.  
  200.     printf("** shouldn't be in %s!\n", __FUNC__);
  201.     closeall(1);
  202.     if (!safecmd(ctlcmd)) {
  203.         (void) fprintf(stderr,
  204.             "%s: control `%s' looks unsafe to execute\n", progname, ctlcmd);
  205.         _exit(1);
  206.     }
  207.     cmd = malloc((unsigned) STRLEN("PATH=") + strlen(ctlfile("bin")) +
  208.         STRLEN(":") + strlen(SUBDIR) + STRLEN(";") + strlen(ctlcmd) +
  209.         STRLEN(" <") + strlen(inname) + 1);
  210.     if (cmd == NULL) {
  211.         warning("can't allocate memory in runctlmsg", "");
  212.         _exit(1);
  213.     }
  214.     (void) strcpy(cmd, "PATH=");
  215.     (void) strcat(cmd, ctlfile("bin"));
  216.     (void) strcat(cmd, ":");
  217.     (void) strcat(cmd, SUBDIR);
  218.     (void) strcat(cmd, ";");
  219.     (void) strcat(cmd, ctlcmd);
  220.     (void) strcat(cmd, " <");
  221.     (void) strcat(cmd, inname);
  222.  
  223.     cmdstat = system(cmd);
  224.     if (cmdstat != 0)
  225.         bombctlmsg(cmd, cmdstat);
  226.     free(cmd);
  227.     _exit(0);
  228. }
  229.  
  230. /*
  231.  * Notify the local news administrator by mail that "cmd" failed
  232.  * with "cmdstat" status, and _exit with bad status (again avoid stdio
  233.  * buffer flushing in the child).
  234.  */
  235.  
  236. STATIC void bombctlmsg(cmd, cmdstat)
  237. char *cmd;
  238. int cmdstat;
  239. {
  240.     register char *mailcmd;
  241.     register FILE *mailf;
  242.     
  243.     printf("** shouldn't be in %s!\n", __FUNC__);
  244.     mailcmd = malloc((unsigned)STRLEN("PATH=") + strlen(newspath()) +
  245.         STRLEN(" mail ") + strlen(newsmaster()) + 1);
  246.     if (mailcmd == NULL) {
  247.         warning("can't allocate memory in bombctlmsg", "");
  248.         _exit(1);
  249.     }
  250.     (void) sprintf(mailcmd, "PATH=%s mail %s", newspath(), newsmaster());
  251.     mailf = popen(mailcmd, "w");
  252.     if (mailf == NULL)
  253.         mailf = stderr;
  254.     (void) fprintf(mailf,
  255.         "%s: control message `%s' exited with status 0%o\n",
  256.         progname, cmd, cmdstat);
  257.     if (mailf != stderr)
  258.         (void) pclose(mailf);
  259.     free(mailcmd);
  260.     _exit(1);
  261. }
  262.